Android Interview - 2
思维导图,用于面试前复习Android的基础知识点,用自己的话总结出面试要回答到的重点,可能包括一些不包括该知识点的全面概括与解析,其中也包括了面试中被问到的知识。
Activity
Activity的四种启动模式
- standard 标准,默认,先进后出
- singleTop (栈顶复用模式)当再次启动的界面位于栈顶,复用该实例,当再次启动的界面不位于栈顶,重新创建实例
- singleTask (栈内复用模式)具有clearTop的功能
- singleInstance (栈内唯一实例),系统没有该实例,新建一个任务栈,并将放入栈顶;系统有实例,使用该实例并转到前台
Activity的启动流程
- Activity的管理核心是AMS(ActivityManagerService),是一个独立的进程,ActivtyThread是每一个应用程序所在进程的主线程,循环的处理消息。他们之间通过binder来进行进程间的通信。
- 流程
- AMS发现这个activity还没启动,就会通知zygot孵化出应用进程
- 然后在dalvik应用进程里执行ActivityThread的main方法
- 应用进程会通知AMS已经启动,AMS保存了一个应用进程的代理对象,这样AMS可以通过代理对象控制应用进程,AMS然后通知创建入口Activity实例,并执行他的生命周期方法
Activity的通信方式
- startActivityForResult
- EventBus
- LocalBroadcastReceiver
Service
两种启动方式
- bindservice: 绑定服务,调用者挂了,服务也会挂掉。绑定者可以调用服务里面的方法。
- startservice:开启者挂了,服务还在后台长期的运行。
开启者不能调用服务里面的方法。
IntentService : 会创建独立的worker线程来处理OnHandlerIntent()方法实现的代码,无需处理多线程的问题所有请求处理完成后,IntentService会自动停止。
与Activity的通信方式
- Callback加接口
- 用Service发广播,activity接收广播
- 可以通过bindService的方式,先在Activity里实现一个ServiceConnection接口,并将该接口传递给bindService()方法,在ServiceConnection接口的onServiceConnected()方法里执行相关操作。
BroadcastReciver
- 接收原理
- 通过Binder机制向ActivityMangerService注册广播
- 通过Binder机制向ActivityMangerService发送广播
- AMS查找符合相应条件的广播,将广播发送到BroadcastReceiver所在的消息队列中。
- BroadcastReceiver所在消息队列拿到此广播后,回调它的onReceive()方法
- 广播的分类与注册方式
- 分类:分为有序广播或者无序广播,有序广播的广播接收者按照Priority属性值从大-小排序接收。
- 注册:动态注册是在代码中注册,跟随组件的生命周期,组件结束前需要动态移除,防止内存溢出。静态注册即使app退出还是可以接收,占内存,耗电大。
ContentProvider
- ContentProvider中的数据监控机制与Android系统中的广播机制的区别
- 前者通过URI来把通知的发送者和接收者关联在一起,后者是通过Intent
- 前者通知注册中心是由ContentService服务来扮演,后者是AMS服务来扮演
- 前者接收数据更新通知要继承ContentObserver,后者继承BroadcastReceiver
Fragment
- 通信方式
- Fragment调用Activity时,直接使用getActivity()拿到Activity实例
- Actvity调用Fragment时,采用接口回调的形式
- Fragment调用Fragment时,用FindFragmentById()
Handler机制
角色
- Message:消息,线程间通讯的数据单元。
- MessageQueue:消息队列,主要用来向消息队列添加消息和取出消息,先进先出。
- Looper:消息循环器,主要用来把消息分发给相应的处理者。
- Handler:消息处理器,主要向消息队列发送各种消息以及处理各种消息。
流程
- Handler通过sendMessage()发送消息Message到消息队列MessageQueue。
- Looper通过loop()不断提取消息,并且将Messeage交给目标handler来进行处理
- 调用自身的回调方法handleMessage()来处理Message
事件分发
- 事件分发的三个核心方法:dispatchTouchEvent(),onInterceptTouchEvent(),onTouchEvent()。
- 事件分发的顺序:Activity(Window) -> ViewGroup -> View
事件分发核心伪代码:
1
2
3
4
5
6
7
8
9
10
11//父View调用方法入口进行事件分发
public boolean dispatchTouchEvent(MotionEvent motionEvent){
boolean consume = false;
//是否进行拦截
if(onInterceptTouchEvent(event)){
consume = onTouchEvent(event);
}else{
consume = child.dispatchTouchEvent(event);
}
return consume;
}
View的绘制
- 绘制流程
- onMeasure():测量视图,从顶层父view到子view递归调用measure()方法,完成绘制
- onLayout():确定视图的位置,从顶层父View到子View递归调用layout()方法,父View将上一步measure()方法得到的子View的布局大小和布局参数,将子View放在合适的位置上
- onDraw():绘制最终的视图,首先ViewRoot创建一个Canvas对象,然后调用onDraw()方法进行绘制。onDraw()方法的绘制流程为:① 绘制视图背景。② 绘制画布的图层。 ③ 绘制View内容。④ 绘制子视图,如果有的话。⑤ 还原图层。⑥ 绘制滚动条。
APK打包流程与安装流程
打包流程
主要是资源打包和代码打包
- 通过aapt工具将资源文件(包括AndroidManifest.xml、布局文件、各种xml资源等)打包成R.java文件
- 处理AIDL文件,生成对应的Java文件
- Javac工具编译项目源码,生成class文件
- DX工具将class文件转换成dex文件,主要完成java字节码转换成Dalvik字节码
- 通过ApkBuilder工具将资源文件、DEX文件打包生成APK文件
- 利用keystore生成签名文件
- 正式版本apk还会使用ZipAlign工具进行对齐处理,可以加快内存映射访问APK文件
的速度。
安装流程
复制APK到/data/app目录下
- 资源管理器解析APK里的资源文件
- 解析AndroidManifest文件,并在data/data目录下创建对应的应用数据目录
- 然后对dex文件进行优化,保存在dalvik-cache目录下
- 解析AndroidManifest文件,创建出四大组件,注册到PackageManagerService中
- 安装完成后发送安装完成的广播(AppReceiver-PACKAGE_ADDED)。
Binder
Android Binder是用来做进程通信的,Android的各个应用以及系统服务都运行在独立的进程中,它们的通信都依赖于Binder。
Linux进程通讯的方式有哪些
- 管道:在创建时分配一个page大小的内存,缓存区大小比较有限
- 消息队列:信息复制两次,额外的CPU消耗;不合适频繁或信息量大的通信;
- 共享内存:无须复制,共享缓冲区直接付附加到进程虚拟地址空间,速度快;但进程间的同步问题操作系统无法实现,必须各进程利用同步工具解决;
- Socket:作为更通用的接口,传输效率低,主要用于不通机器或跨网络的通信;
- 信号量:常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
- 信号: 不适用于信息交换,更适用于进程中断控制,比如非法内存访问,杀死某个进程等;
Binder的好处
- 高性能:从数据拷贝次数来看Binder只需要进行一次内存拷贝,而管道、消息队列、Socket都需要两次,共享内存不需要拷贝,Binder的性能仅次于共享内存。
- 稳定:Binder基于C/S架构,客户端与服务端彼此独立,稳定性较好。
- 安全性:Android为每个应用分配了UID,用来作为鉴别进程的重要标志,Android内部也依赖这个UID进行权限管理。标记完全是在用户空间控制的,没有放在内核空间,因此有被恶意篡改的可能,因此Binder的安全性更高。
常见的内存泄露
- 持有静态的Context(Activity)引用
- 持有静态的View引用
- 不正确的单例模式,比如单例持有Activity或者Context
- bitmap,io等资源的回收
- 内部类&匿名内部类实例无法释放,而内部类又持有外部类的强引用,导致外部类无法释放
MVC,MVP,MVVM对比分析
- MVC:Activity/Fragment即是View也是,Controller,项目越大,耦合越大。
- MVP:为了达到View和Controller进行解耦,引入了Presenter进行解耦,将视图逻辑和业务逻辑进行分离
- MVVM:使用ViewModel代替Presenter,实现数据与View的双向绑定,可以使用data-bindging将数据绑定进xml中
热修复的原理
- 利用PathClassLoader和DexClassLoader去加载与bug类同名的类,替换掉bug类,进而达到修复bug的目的,原理是在app打包的时候阻止类打上CLASS_ISPREVERIFIED的标志,然后在热修复的时候动态修改BaseDexClassLoader对象间接引用的dexElements,替换掉旧的类.
Parcelable和Serializable区别
- 序列化的两种方式,序列化就是将对象变成二进制流,便于存储和传输。
- Serializable是java实现的一套序列化方式,可能会触发频繁的IO操作,效率比较低,适合将对象存储到磁盘上的情况。
- Parcelable是Android提供一套序列化机制,它将序列化后的字节流写入到一个共享内存中,其他对象可以从这块共享内存中读出字节流,并反序列化成对象。因此效率比较高,适合在对象间或者进程间传递信息。
线程池
- Android中常见的4种线程池
- FixedThreadPool: 线程数量固定的线程池,它只有核心线程
- CachedThreadPool: 线程数量不固定的线程池,它只有非核心线程
- ScheduledThreadPool: 核心线程数量固定,非核心线程数量没有限制的线程池,主要用于执行定时任务和具有固定周期的任务
- SingleThreadPool: 只有一个核心线程的线程池,确保了所有的任务都在同一个线程中按顺序执行。
进程保活
- 提升进程的优先级,降低进程被杀死的概率,监控手机锁屏事件,在屏幕锁屏时启动一个像素的Activity,在用户解锁时将Activity销毁掉,前台Activity可以将进程变成前台进程,优先级升级到最高。
- 拉活已经被杀死的进程,利用广播拉活Activity。